//
//  Connect.m
//  EFExtend
//
//  Created by mac on 15/6/29.
//  Copyright (c) 2015年 Eric. All rights reserved.
//

#import "Connect.h"
#import <CocoaAsyncSocket/GCDAsyncSocket.h>

@interface Connect ()<GCDAsyncSocketDelegate>
@property (strong, nonatomic) GCDAsyncSocket *  socket;

@end

@implementation Connect{

    NSTimer *       timer;
    BOOL            readyToClose;
    NSTimer *       timerRetry;
    NSTimeInterval  retryTimes;
    unsigned char   Buffer[1024];
    NSUInteger      bufferLeaved;
    NSLock *        bufferLock;
    NSUInteger      readCount;
    NSInteger       retryCount;
}
@synthesize socket=_socket;

DEF_NOTIFICATION(SOCKETRETRY)
DEF_NOTIFICATION(SOCKETTIMEOUT)

#pragma mark - lifestyle

- (void)timetoReconnect:(NSTimer*)time{
    
    if (readyToClose==NO) {
        [self setupSocket];
    }
    
}

- (BOOL)setupSocket{
#if DEBUG
    INFO(@"service setupSocket");
#endif
    if (_socket==nil) {
        
        _socket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
        
    }
    if (_socket.isDisconnected) {
        NSError *err = nil;
        if(![_socket connectToHost:self.TCPHost onPort:self.TCPPort withTimeout:10.f error:&err]){

            INFO(@"connect error:%@",err);
            
            return NO;
        }else{
            
        }
    }else{
        [_socket writeData:[self requestVideo] withTimeout:20.f tag:1];
    }
    
    return YES;
}

#pragma mark -

- (instancetype)init
{
    self = [super init];
    if (self) {
        
    }
    return self;
}

- (void)dealloc
{
    [self closeService];
    HM_SUPER_DEALLOC();
}

- (BOOL)action{
    
    readyToClose = NO;
    
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(closeService) object:self];
    
#if DEBUG
    INFO(@"Radio service action");
#endif

    [self setupSocket];
    
    return YES;
}

- (void)deaction{
    
    readyToClose = YES;
    
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(closeService) object:self];
    [self performSelector:@selector(closeService) withObject:self afterDelay:3];
    [_socket writeData:[self stopVideo] withTimeout:20.f tag:0];

    
}

-(void)closeService{
    
    
    [timer invalidate];
    timer = nil;
    
    [timerRetry invalidate];
    timerRetry = nil;
    
    [_socket disconnect];
    _socket = nil;
}


#pragma mark - delegate

-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{
    
#if DEBUG
    INFO(@"connect to %@:%d OK",sock.connectedHost,sock.connectedPort);
    
#endif
    retryCount = 0;
    
    [timerRetry invalidate];
    timerRetry = nil;

    [_socket writeData:[self stopVideo] withTimeout:20.f tag:0];
    [_socket writeData:[self requestVideo] withTimeout:20.f tag:1];
}

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
    
#if DEBUG
    INFO(@"[%ld]<%.2f>disconnect------errC:%ld   %@",retryCount,retryTimes,err.code,err.localizedDescription);
    
#endif

    if (readyToClose) {
        return;
    }
    //重连
    NSTimeInterval retry=5;
    if (err) {
        switch (err.code) {
            case GCDAsyncSocketClosedError:
            case 61:{//服务器没开－不能到达
                retry = 10;
            }
                break;
            case 65:{//本地网络没开－不能到达
                retry = 5;
            }
                break;
            case GCDAsyncSocketConnectTimeoutError:{//连接超时了
                retry = 5;
            }
                break;
            case GCDAsyncSocketReadTimeoutError:
                
            case GCDAsyncSocketWriteTimeoutError:
                
            case GCDAsyncSocketReadMaxedOutError:
                
            default:
                retry = 5;
                break;
        }
    }
    if (readyToClose==NO) {
        retryCount++;
        retry*=(1+retryCount/5);
        
        if (retryCount%10==0) {
            [self postNotification:[Connect SOCKETRETRY] withObject:self];
        }
        
        if ((retryTimes != retry)||(timerRetry==nil)) {
            
            retryTimes = retry;
            
            if (sock.isDisconnected) {
                
                [timer invalidate];
                timer = nil;
                
                [timerRetry invalidate];
                timerRetry = nil;
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    if (timerRetry==nil) {
                        timerRetry = [NSTimer scheduledTimerWithTimeInterval:retryTimes target:self selector:@selector(timetoReconnect:) userInfo:self repeats:YES];
                    }
                });
            }
        }
    }
    
}
- (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length{
    if (tag==1) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self postNotification:[Connect SOCKETTIMEOUT]];
        });
    }
    
    return 0;
}
- (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length{
    dispatch_async(dispatch_get_main_queue(), ^{
        [self postNotification:[Connect SOCKETTIMEOUT]];
    });
    return 0;
}
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
    readCount++;
    [sock readDataWithTimeout:-1 tag:tag];
}

-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    INFO(@"connect receive data from",sock.connectedHost);
    if (readyToClose) {
        return;
    }
    if (readCount) {
        readCount--;
    }
    if (readCount==0) {
        [sock readDataWithTimeout:-1 tag:tag];
    }
    
}

#pragma mark - request
/*2.3注册到前端*/
- (NSData*)requestLogin{
    NSString * uuid = self.deviceId;
    
    char info[32] = {0};
    int offset = (int)uuid.length;
    memcpy(&info[0], [uuid UTF8String], offset);
    offset = 20;
    /*通道号*/
    [self write2Word:1 to:info offset:offset];
    offset+=4;
    /*ip*/
    NSArray *host = [self.videoHost componentsSeparatedByString:@"."];
    [self write2Word:[host[0] intValue]+[host[1] intValue]&0xff00+[host[2] intValue]&0xff0000+[host[3] intValue]&0xff000000 to:info offset:offset];
    offset+=4;
    
    [self write2Word:self.videoPort to:info offset:offset];
    offset+=4;
    
    return [self buildSendData:&info[0] leng:offset method:0x01 command:0x1004];
}
/*2.5请求前端发送视频数据*/
- (NSData*)requestVideo{
    NSString * uuid = self.deviceId;

    char info[32] = {0};
    int offset = (int)uuid.length;
    memcpy(&info[0], [uuid UTF8String], offset);
    offset = 20;
    /*通道号*/
    [self write2Word:1 to:info offset:offset];
    offset+=4;
    /*ip*/
    NSArray *host = [self.videoHost componentsSeparatedByString:@"."];
    [self write2Word:[host[0] intValue]+[host[1] intValue]&0xff00+[host[2] intValue]&0xff0000+[host[3] intValue]&0xff000000 to:info offset:offset];
    offset+=4;
    
    [self write2Word:self.videoPort to:info offset:offset];
    offset+=4;
    
    return [self buildSendData:&info[0] leng:offset method:0x01 command:0x1005];
}
/*2.5请求前端停止发送视频数据*/
- (NSData*)stopVideo{
    NSString * uuid = self.deviceId;

    char info[32] = {0};
    int offset = (int)uuid.length;
    memcpy(&info[0], [uuid UTF8String], offset);
    offset = 20;
    /*通道号*/
    [self write2Word:1 to:info offset:offset];
    offset+=4;
    /*预留*/
    offset+=8;
    
    return [self buildSendData:&info[0] leng:offset method:0x01 command:0x1006];
}

#pragma mark base

- (void)writeWord:(int)word to:(char*)bytes offset:(int)offset{
    bytes[offset] = word&0x00ff;
    bytes[offset+1] = (word&0xff00)>>16;
}

- (void)write2Word:(long long)word to:(char*)bytes offset:(int)offset{
    bytes[offset] = word&0x00ff;
    bytes[offset+1] = (word&0xff00)>>16;
    bytes[offset+2] = (word&0xff0000)>>32;
    bytes[offset+3] = (word&0xff000000)>>48;
}

- (NSData *)buildSendData:(const char *)string leng:(int)leng method:(int)method command:(int)command{
    
    int length = leng;
    int bit = 0;
    length += 16;
    
    char *c=malloc(length);
    if (c==NULL) {
        return nil;
    }
    memset(c, 0, length);
    
    //版本号0x1001
    [self writeWord:18006 to:c offset:bit];
    bit+=2;
    
    //长度
    [self writeWord:(length - 2) to:c offset:bit];
    bit +=2;
    
    //消息类型
    [self writeWord:command to:c offset:bit];
    bit+=2;
    
    //消息标识
    /*
     0x01：请求
     0x02：需要回复
     0x04：回复
     0x08：上传
     */
    [self writeWord:method to:c offset:bit];
    bit+=2;
    
    //序列号
    static long long serial = 0;
    serial++;
    [self write2Word:serial to:c offset:bit];
    bit+=4;
    
    memcpy(&c[bit], string , leng);
    bit += leng;
    
#if DEBUG
    printf("sending 0x%04x:",(unsigned char)command);
    for (int a=0; a<length; a++) {
        printf("%02x",(unsigned char)c[a]);
    }
    printf("\n");
#endif
    
    NSData *data = [NSData dataWithBytes:c length:length];
    free(c);
    
    return data;
}

@end
